﻿using Business.Base;
using Business.Base.Common;
using Business.Base.DB;
using Business.Base.Enums;
using Business.Base.Model;
using Business.Base.Service;

using Configuration;
using PLC;
using PLC.Base;
using PLC.Standard;
using PumpAlgorithm;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Business.CentrifugalPump.Perf

{
    public class CentrifugalPumpPerfExperimentCaculateService : ICaculateService
    {
        #region 私有函数
        /// <summary>
        /// 返回列表中的最大值和最小值，第一个是最小值，第二个是最大值
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private double[] getPeak(Experiment exp)
        {
            CentrifugalPumpPerfExperiment experiment = (CentrifugalPumpPerfExperiment)exp;
            List<ExperimentRecord> list = experiment.RecordList;
            if (list == null || list.Count == 0)
            {
                return null;
            }

            Double max, min;
            min = Double.MaxValue;
            max = Double.MinValue;

            Double[] flowList = new Double[list.Count];
            for (int i = 0; i < list.Count; i++)
            {
                CentrifugalPumpPerfExperimentRecord record = (CentrifugalPumpPerfExperimentRecord)list[i];
                flowList[i] = record.StpFlow;


                //找出最大最小的压力值
                if (flowList[i] - max > Double.Epsilon)
                {
                    max = flowList[i];
                }
                if (min - flowList[i] > Double.Epsilon)
                {
                    min = flowList[i];
                }
            }
            return new double[] { min, max };
        }

        /// <summary>
        /// 求流量扬程，流量功率，流量效率拟合曲线的参数
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private List<Double[]> calculateParams(Experiment exp)
        {
            CentrifugalPumpPerfExperiment experiment = (CentrifugalPumpPerfExperiment)exp;
            List<ExperimentRecord> list = experiment.RecordList;
            if (list == null || list.Count == 0)
            {
                return null;
            }

            Double[] hList = new Double[list.Count];
            Double[] qList = new Double[list.Count];
            Double[] pList = new Double[list.Count];
            Double[] eList = new Double[list.Count];

            //最终报表中展示的都是标况下的数据
            for (int i = 0; i < list.Count; i++)
            {
                CentrifugalPumpPerfExperimentRecord record = (CentrifugalPumpPerfExperimentRecord)list[i];
                qList[i] = record.StpFlow;
                hList[i] = record.StpHead;

                pList[i] = record.StpShaftPower;
                eList[i] = record.StpPumpEfficiency;
            }


            Double[] paramH = CanonicalAlgorithm.Solve(qList, hList);
            Double[] paramP = CanonicalAlgorithm.Solve(qList, pList);
            Double[] paramE = CanonicalAlgorithm.Solve(qList, eList);

            List<Double[]> paramsList = new List<Double[]>();
            paramsList.Add(paramH);
            paramsList.Add(paramP);
            paramsList.Add(paramE);

            return paramsList;
        }

        /// <summary>
        /// 求以下两个曲线的焦点：
        /// 1. 额定流量和扬程的曲线 y=ax (其中a = ratedHead/ratedFlow) 
        /// 2. 拟合的流量扬程曲线 y = paramQ[0] * sqrt(x,4) +  paramQ[1] * sqrt(x,3)+paramQ[2] * sqrt(x,3)+ paramQ[3] * sqrt(x,2)+ paramQ[1] * sqrt(x,1) + paramQ[0]
        /// 该交点称作流量和扬程实测的坐标点
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="paramQ"></param>
        /// <returns></returns>
        private Double[] calculateCrossFlowHead(Experiment exp, Double[] paramQ)
        {
            Double minFlow, maxFlow;
            Double[] peak = getPeak(exp);
            minFlow = peak[0];
            maxFlow = peak[1];
            CentrifugalPumpPerfExperiment experiment = (CentrifugalPumpPerfExperiment)exp;
            ///求以下两个曲线的焦点：
            /// 1. 额定压力和流量的曲线 y=ax (其中a = ratedFlow/ratedPresure) 
            /// 2. 拟合的压力流量曲线 y = paramQ[0] * sqrt(x,4) +  paramQ[1] * sqrt(x,3)+paramQ[2] * sqrt(x,3)+ paramQ[3] * sqrt(x,2)+ paramQ[1] * sqrt(x,1) + paramQ[0]
            /// 该交点称作压力和流量实测的坐标点
            //Double[] crossFlowValue = CanonicalAlgorithm.SolveEquations(experiment.PumpRatedHead/experiment.PumpRatedFlow,0, minFlow, maxFlow, paramQ);
            List<Double[]> crossFlowValue = CanonicalAlgorithm.SolveEquations(experiment.PumpRatedHead / experiment.PumpRatedFlow, 0, minFlow, maxFlow, paramQ);
            
           
            Double flow = Double.NaN;
            Double head = Double.NaN;
            if (crossFlowValue.Count==1)
            {
                flow = crossFlowValue[0][0];
                head = crossFlowValue[0][1];
                return new double[] { flow, head };
            }
            else
            {
                return null;
            }
            
        }
        #endregion

        /// <summary>
        /// 返回试验的结论性数据，这里的结论数据包括如下：
        /// 1. 实测流量（MeasureFlow）和扬程（MeasureHead）
        ///    计算逻辑是将实测数据转化成标况数据的拟合曲线与额定流量曲线，扬程的曲线（head = ARate*flow）的交点
        ///    如果在拟合曲线范围内没有交点，则所有的结论性的数据都为NA。   
        /// 2. 根据实测流量的数据带入流量功率，流量效率的曲线，计算出实测的功率和实测的效率
        /// 3. 根据试验标准ExperimentLevel给出的范围，判断泵的实测数据是否在标准范围以内，并且将判断结果赋值给MeasurePowerIsPass，MeasureEffIsPass

        public void ReCaculateRecord(Experiment exp)
        {
            for (int i = 0; i < exp.RecordList.Count; i++)
            {
                ExperimentRecord record = exp.RecordList[i];
                EnvData data = new EnvData();
                data.Current = record.ElectricCurrent;
                data.Voltage = record.Voltage;
                data.Flow = record.WorkingFlow;
                data.Temperature = record.Temperature;
                data.PresureIn = record.PresureIn;
                data.PresureOut = record.PresureOut;
                data.ShaftPower = record.WorkingPower;
                data.Speed = record.WorkingSpeed;
                ExperimentRecord newRecord = CaculateDataFromRecord(exp, data);
                exp.RecordList[i] = newRecord;
            }
        }
        /// </summary>
        /// <param name="exp"></param>
        public void CaculateExperimentResult(ref Experiment exp)
        {
            CentrifugalPumpPerfExperiment experiment = (CentrifugalPumpPerfExperiment)exp;


            #region 计算流量扬程，流量功率，流量效率的拟合曲线参数
            ///计算流量扬程，流量功率，流量效率的拟合曲线参数
            List<Double[]> listParams = calculateParams(exp);
            List<ExperimentRecord> list = experiment.RecordList;
            if (list == null || list.Count == 0)
            {
                return;
            }

            
            Double[] paramH = listParams[0];
            Double[] paramP = listParams[1];
            Double[] paramE = listParams[2];
            #endregion

            //计算实测流量和扬程
            double[] flowHead = calculateCrossFlowHead(exp, paramH);
            if (flowHead != null)
            {
                experiment.MeasureFlow = flowHead[0];
                experiment.MeasureHead = flowHead[1];


                experiment.MeasureFlowIsPass = StandLevelJudger.JudgeFlowLevel(experiment.MeasureFlow, experiment.PumpRatedFlow, experiment.ExperimentLevel);
                experiment.MeasureHeadIsPass = StandLevelJudger.JudgeHeadLevel(experiment.MeasureHead, experiment.PumpRatedHead, experiment.ExperimentLevel);

                /// 将额压力和流量实测的坐标点的压力值带入压力功率拟合曲线，求出实测功率
                Double measuredPower = CanonicalAlgorithm.CaculateValue(experiment.MeasureFlow, paramP);
                bool powerLevel = StandLevelJudger.JudgePowerLevel(measuredPower, experiment.PumpRatedPower, experiment.ExperimentLevel);
                experiment.MeasurePower = (float)measuredPower;
                experiment.MeasurePowerIsPass = powerLevel;

                /// 将额压力和流量实测的坐标点的压力值带入压力效率拟合曲线，求出实测效率
                Double measuredEff = CanonicalAlgorithm.CaculateValue(experiment.MeasureFlow, paramE);
                bool effLevel = StandLevelJudger.JudgeEffLevel(measuredEff, experiment.PumpRatedEfficiency, experiment.ExperimentLevel);
                experiment.MeasureEff = (float)measuredEff;
                experiment.MeasureEffIsPass = effLevel;
            }
            else
            {
                //如果无法获取到交点，则无法获得结果
                experiment.MeasureEff = experiment.MeasureHead = experiment.MeasurePower = experiment.MeasureFlow = Double.NaN;
                experiment.MeasureFlowIsPass = experiment.MeasurePowerIsPass = experiment.MeasureHeadIsPass = experiment.MeasureEffIsPass = false;
            }
          

        }
               
        public ExperimentRecord CaculateDataFromRecord(Experiment exp, EnvData data)
        {
            CentrifugalPumpPerfExperiment experiment = (CentrifugalPumpPerfExperiment)exp;
            CanonicalAlgorithm canonicalAlgorithm = new CanonicalAlgorithm(
                experiment.ZInlet,
                experiment.ZOutlet,
                experiment.KInlet,
                experiment.KOutlet,
                experiment.DInlet,
                experiment.DOutlet,
                experiment.LInlet,
                experiment.LOutlet,
                experiment.MotorRatedEfficiency,
                experiment.MotorRatedSpeed
                );
            canonicalAlgorithm.setplcEnvData(data);
            canonicalAlgorithm.Calculate();

            CentrifugalPumpPerfExperimentRecord record = new CentrifugalPumpPerfExperimentRecord();
            record.ElectricCurrent = data.Current;
            record.Voltage = data.Voltage;
            record.WorkingFlow = data.Flow;
            record.Temperature = data.Temperature;
            record.PresureIn = data.PresureIn;
            record.PresureOut = data.PresureOut;
            record.WorkingPower = data.ShaftPower;
            record.WorkingSpeed = data.Speed;

            record.WorkingHead = canonicalAlgorithm.WorkingHead;
            record.StpFlow = canonicalAlgorithm.StpQ;
            record.StpHead = canonicalAlgorithm.StpHead;
            record.StpPowerIn = canonicalAlgorithm.StpPowerIn/1000.0f;
            record.StpShaftPower = canonicalAlgorithm.StpShaftPower / 1000.0f; ;
            record.StpPumpEfficiency = canonicalAlgorithm.StpPumpEfficiency;
            record.StpGroupEfficiency = canonicalAlgorithm.StpGroupEfficiency;
            record.WorkingStaticHead = canonicalAlgorithm.WorkingStaticHead;
            record.WorkingDynamicHead = canonicalAlgorithm.WorkingDynamicHead;
            record.WorkingGeoHead = canonicalAlgorithm.WorkingGeoHead;
            record.WorkingFrictionHead = canonicalAlgorithm.WorkingFrictionHead;       

            return record;
        }

        public BaseChartData CaculateChartData(Experiment exp)
        {
            CentrifugalPumpPerfChartData chartDataInfo = new CentrifugalPumpPerfChartData();

            //获取试验对象的额定数据以及试验标准。
            CentrifugalPumpPerfExperiment experiment = (CentrifugalPumpPerfExperiment)exp;
            List<ExperimentRecord> list = experiment.RecordList;

            #region 初始化变量
            Double ratedFlow = experiment.PumpRatedFlow;
            Double ratedHead = experiment.PumpRatedHead;
            Double ratedPower = experiment.PumpRatedPower;
            Double rateEff = experiment.MotorRatedEfficiency;
            FlowHeadLevel level = experiment.ExperimentLevel;

            //x，y轴上的坐标单元数量
            int xUnitCount = Config.XAxisUnit;
            int yUnitCount = Config.YAxisUnit;

            //坐标空间的余量
            double axisMaxRate = Config.AxisMaxRate;

            //画曲线的时候的取值数量
            int drawPointCount = Config.DrawPointCount;            

            //设置坐标轴的最小值
            chartDataInfo.MinXQAxis = 0.0f;
            chartDataInfo.MinYHAxis = 0.0f;
            chartDataInfo.MinYPAxis = 0.0f;
            chartDataInfo.XRatedQ = ratedFlow;
            chartDataInfo.YRatedHead = ratedHead;
            chartDataInfo.YRatedPower = ratedPower;
            chartDataInfo.YRatedEff = rateEff;
            chartDataInfo.XUnitCount = xUnitCount;
            chartDataInfo.YUnitCount = yUnitCount;
            #endregion

            //如果还没有采集数据，则设置流量，功率,扬程最大值为额定值除以axisMaxRate，axisMaxRate默认配置为80%，保证最大值为额定值的时候，让额定值展示在坐标轴80%的地方。
            if (list.Count == 0)
            {
                    chartDataInfo.MaxXQAxis = ratedFlow / axisMaxRate; ;
                    chartDataInfo.MaxYHAxis = ratedHead / axisMaxRate; ;
                    chartDataInfo.MaxYPAxis = ratedPower / axisMaxRate ; 

            }
            else
            {
                #region  计算初始化
                //Double[] qList = new Double[list.Count];
                //Double[] hList = new Double[list.Count];
                //Double[] pList = new Double[list.Count];
                //Double[] eList = new Double[list.Count];

                //获取最小流量和最大流量
                double maxQ = 0.0f;
                double minQ = Double.MaxValue;

                //获取扬程的最大值和最小值
                Double maxHY = 0.0f;
                Double minHY = Double.MaxValue;

                //获取Y轴数据的最大值和最小值
                Double maxPY = 0.0f;
                Double minPY = Double.MaxValue;

                Double maxAxisQ     = ratedFlow;
                Double maxAxisHead  = ratedHead;
                Double maxAxisPower = ratedPower;
                //Double maxAxisEff   = 100;



                #endregion

                #region 将实验记录转化成数组，用于chart图形的展示
                
                List<double[]> pointList = new List<double[]>();
                for (int i = 0; i < list.Count; i++)
                {
                    CentrifugalPumpPerfExperimentRecord record = (CentrifugalPumpPerfExperimentRecord)list[i];
                    double[] data = new double[4] { record.StpFlow, record.StpHead, record.StpShaftPower, record.StpPumpEfficiency };

                    #region 求出流量，扬程，功率，效率的最大值和最小值
                    //流量最大值和最小值
                    #region 注释
                    /*double delta = q - maxQ;
                    if (delta >= 0.0001)
                    {
                        maxQ = q;
                    }
                    delta = q - minQ;
                    if (delta <= -0.0001)
                    {
                        minQ = q;
                    }

                    //扬程最大值和最小值
                    Double hy = (Double)hList[i];
                    delta = hy - maxHY;
                    if (delta >= 0.0001)
                    {
                        maxHY = hy;
                    }
                    delta = hy - minHY;
                    if (delta <= -0.0001)
                    {
                        minHY = hy;
                    }

                    //功率最大最小值
                    Double py = (Double)pList[i];
                    delta = py - maxPY;
                    if (delta >= 0.0001)
                    {
                        maxPY = py;
                    }
                    delta = py - minPY;
                    if (delta <= -0.0001)
                    {
                        minPY = py;
                    }*/
                    #endregion
                    /*
                    double delta = q - maxQ;
                    if (delta > double.Epsilon)
                    {
                        maxQ = q;
                    }
                    delta =  minQ- q;
                    if (delta > double.Epsilon)
                    {
                        minQ = q;
                    }

                    //扬程最大值和最小值
                    Double hy = (Double)hList[i];
                    delta = hy - maxHY;
                    if (delta > double.Epsilon)
                    {
                        maxHY = hy;
                    }
                    delta = minHY - hy;
                    if (delta >Double.Epsilon)
                    {
                        minHY = hy;
                    }

                    //功率最大最小值
                    Double py = (Double)pList[i];
                    delta = py - maxPY;
                    if (delta >Double.Epsilon)
                    {
                        maxPY = py;
                    }
                    delta =  minPY -py;
                    if (delta > Double.Epsilon)
                    {
                        minPY = py;
                    }*/
                    #endregion


                   
                    pointList.Add(data);
                }

                chartDataInfo.InputPoints = pointList;
                #endregion

                #region   在记录中找出扬程，功率，效率的最大值，如果最大值小于额定值，则坐标的最大值为额定功率/axisMaxRate        
                
                maxQ = list.Max<ExperimentRecord>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpFlow);
                minQ = list.Min<ExperimentRecord>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpFlow);
                maxHY = list.Max<ExperimentRecord>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpHead);
                minHY = list.Min<ExperimentRecord>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpHead);
                maxPY = list.Max<ExperimentRecord>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpShaftPower);
                minPY = list.Min<ExperimentRecord>( x => ((CentrifugalPumpPerfExperimentRecord)x).StpShaftPower);
                

                maxAxisQ = ratedFlow - maxQ > double.Epsilon ? ratedFlow : maxQ;
                maxAxisHead = ratedHead - maxHY> double.Epsilon? ratedHead : maxHY;
                maxAxisPower = ratedPower - maxPY > double.Epsilon ? ratedPower : maxAxisPower;

              
                //求出最大值，并且做一个坐标余量的计算。
                chartDataInfo.MaxXQAxis = maxAxisQ / axisMaxRate;
                chartDataInfo.MaxYHAxis = maxAxisHead / axisMaxRate;
                chartDataInfo.MaxYPAxis = maxAxisPower / axisMaxRate;
                #endregion
                
                if (list.Count >2)
                {
                    double[] qList = list.Select<ExperimentRecord, Double>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpFlow).ToList().ToArray();
                    double[] hList = list.Select<ExperimentRecord, Double>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpHead).ToList().ToArray();
                    double[] pList = list.Select<ExperimentRecord, Double>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpShaftPower).ToList().ToArray();
                    double[] eList = list.Select<ExperimentRecord, Double>(x => ((CentrifugalPumpPerfExperimentRecord)x).StpPumpEfficiency).ToList().ToArray();
                    #region 根据测试出的数据拟合出曲线
                    Double[] paramH = CanonicalAlgorithm.Solve(qList, hList);
                    Double[] paramP = CanonicalAlgorithm.Solve(qList, pList);
                    Double[] paramE = CanonicalAlgorithm.Solve(qList, eList);

                    #region 如果计算的值不是数字，则不画曲线
                    Boolean needDrawLine = true;
                    for (int i = 0; i < paramH[i]; i++)
                    {
                        if (Double.IsInfinity(paramH[i]) || Double.IsNaN(paramH[i]))
                        {
                            needDrawLine = false;
                        }
                    }

                    for (int i = 0; i < paramP[i]; i++)
                    {
                        if (Double.IsInfinity(paramP[i]) || Double.IsNaN(paramP[i]))
                        {
                            needDrawLine = false;
                        }
                    }

                    for (int i = 0; i < paramE[i]; i++)
                    {
                        if (Double.IsInfinity(paramE[i]) || Double.IsNaN(paramE[i]))
                        {
                            needDrawLine = false;
                        }
                    }

                    #endregion

                    if (needDrawLine)
                    {
                        #region 生成拟合曲线，写入chartDataInfo
                        //根据公式，平均选取30个点，用于报表中画曲线的点
                        Double[] xValue = new Double[drawPointCount + 1];
                        Double[] yhValue = new Double[drawPointCount + 1];
                        Double[] ypValue = new Double[drawPointCount + 1];
                        Double[] yeValue = new Double[drawPointCount + 1];
                        double dd = (maxQ - minQ) / drawPointCount;
                        for (int i = 0; i <= drawPointCount; i++)
                        {
                            xValue[i] = minQ + i * dd;
                            Double q = xValue[i];
                            yhValue[i] = CanonicalAlgorithm.CaculateValue(q, paramH);
                            ypValue[i] = CanonicalAlgorithm.CaculateValue(q, paramP);
                            yeValue[i] = CanonicalAlgorithm.CaculateValue(q, paramE);
                            chartDataInfo.DrawList.Add(new double[4] { xValue[i], yhValue[i], ypValue[i], yeValue[i] });
                            //System.Console.WriteLine("{0}，{1},{2},{3}", xValue[i], yhValue[i], ypValue[i], yeValue[i]);
                        }
                        #endregion


                        #region  计算流量扬程的实测值，以及根据流量实测值代入拟合曲线求出的实测功率和效率
                        double[] presureFlow = calculateCrossFlowHead(exp, paramH);
                        if (presureFlow != null)
                        {
                            chartDataInfo.MeasureFlow = presureFlow[0];
                            chartDataInfo.MeasureHead = presureFlow[1];

                            /// 将额压力和流量实测的坐标点的压力值带入压力功率拟合曲线，求出实测功率
                            Double measuredPower = CanonicalAlgorithm.CaculateValue(experiment.MeasureFlow, paramP);
                            chartDataInfo.MeasurePower = measuredPower;

                            /// 将额压力和流量实测的坐标点的压力值带入压力效率拟合曲线，求出实测效率
                            Double measuredEff = CanonicalAlgorithm.CaculateValue(experiment.MeasureFlow, paramE);
                            chartDataInfo.MeasureEff = measuredEff;
                        }
                        #endregion

                    }
                    #endregion


                }

                #region 生成允差范围,目前默认只展示流量扬程的允差范围
                chartDataInfo.FlowHeadRange = StandLevelJudger.GetFlowHeadRange(ratedFlow, ratedHead, level);
                chartDataInfo.FlowPowerRange = StandLevelJudger.GetFlowPowerRange(ratedFlow, ratedPower, level);
                chartDataInfo.FlowEffRange = StandLevelJudger.GetFlowEffRange(ratedFlow, rateEff, level);
                chartDataInfo.ShowEffRange = chartDataInfo.ShowPowerRange = false;
                #endregion

                #region 计算流量扬程直线的最大坐标
                double headQ_a = ratedHead / ratedFlow;

                double maxY = headQ_a * chartDataInfo.MaxXQAxis;
                if (maxY - chartDataInfo.MaxYHAxis > Double.Epsilon)
                {
                    double maxX = (1 / headQ_a) * chartDataInfo.MaxYHAxis;
                    chartDataInfo.XMaxHeadinRateHeadQLine = maxX;
                    chartDataInfo.YMaxHeadinRateHeadQLine = chartDataInfo.MaxYHAxis;

                }
                else
                {
                    chartDataInfo.XMaxHeadinRateHeadQLine = chartDataInfo.MaxXQAxis;
                    chartDataInfo.YMaxHeadinRateHeadQLine = maxY;
                }

               
                #endregion

            }
            return chartDataInfo;
        }
    }
}

